import BinaryWriter from "../binary/BinaryWriter"
import { ValueType, AnyValueType } from "./ObjTypeDef"

const VT = ValueType
const AVT = AnyValueType

/** 对象编码 */
export default function encodeObj<T extends Object>(obj: T, struct: any[]): Uint8Array {
    let w = BinaryWriter.alloc()
    enStruct(w, obj, struct)
    return w.end()
}

/**
 * 对象编码(额外处理)
 * @param beforeStart 开始前的处理
 */
export function encodeObjDeal<T extends Object>(obj: T, struct: any[], beforeStart: (w: BinaryWriter) => any): Uint8Array {
    let w = BinaryWriter.alloc()
    beforeStart(w)
    enStruct(w, obj, struct)
    return w.end()
}

/** 结构编码 */
function enStruct(writer: BinaryWriter, obj: any, struct: any[]) {
    let w = BinaryWriter.alloc()
    let sLen = struct.length

    //标志位，长度每8个字段增长1字节
    //用于判断bit对应字段是否有值，或者bool的真假
    let flag = []
    let fLen = sLen / 8 >> 0
    for (let i = fLen + 1; i--;) {
        flag.push(0)
    }

    //开始遍历字段 idx为第几个字段
    for (let idx = 0; idx < sLen; idx++) {
        let [field, vt] = struct[idx]

        if (vt <= VT.arr) {
            //======== 编码有类型对象、any类型对象和数组 ========
            let val = obj[field]
            if (val) {
                //有值时把标志位对应bit设为1
                flag[idx / 8 >> 0] |= 1 << (idx % 8)

                //[写入] >>> 值
                switch (vt) {
                    case VT.byte:
                        w.byte = val
                        break
                    case VT.uint8:
                        w.uint8 = val
                        break
                    case VT.uint16:
                        w.uint16 = val
                        break
                    case VT.uint32:
                        w.uint32 = val
                        break
                    case VT.uint:
                        w.uint = val
                        break
                    case VT.int8:
                        w.int8 = val
                        break
                    case VT.int16:
                        w.int16 = val
                        break
                    case VT.int32:
                        w.int32 = val
                        break
                    case VT.int:
                        w.int = val
                        break
                    case VT.float:
                        w.float = val
                        break
                    case VT.str:
                        w.str = val
                        break
                    case VT.struct:
                        let _struct = struct[idx][2]
                        enStruct(w, val, _struct)
                        break
                    case VT.obj:
                        enObj(w, val)
                        break
                    case VT.arr:
                        enArr(w, val)
                        break
                }
            }
        } else {
            //======== 编码有类型数组 ========
            let arr: any[] = obj[field]
            if (arr && arr.length) {
                //有长度时把标志位对应bit设为1
                flag[idx / 8 >> 0] |= (1 << idx % 8)

                //递归遍历数组维度
                let vecMax = struct[idx][2]
                let fn = function (write: (arr: any[]) => void, _arr: any[] = arr, vec: number = 1) {
                    //[写入] >>> 数组长度
                    w.uint = _arr.length
                    if (vec++ < vecMax) {
                        //递归
                        _arr.forEach(a => fn(write, a, vec))
                    } else {
                        //写入值
                        write(_arr)
                    }
                }
                let fn2 = function (write: (val: any) => void) {
                    fn(arr => arr.forEach(val => write(val)))
                }
                //[写入] >>> 值
                switch (vt) {
                    case VT.boolArr:
                        fn(arr => {
                            //bool标志位，用来判断bit对应bool的真假
                            let bFlag = [0]
                            let bFLen = arr.length / 8 >> 0
                            for (let i = bFLen + 1; i--;) {
                                bFlag.push(0)
                            }
                            //值为true时把标志位对应bit设为1
                            arr.forEach((val, idx) => val && (bFlag[idx / 8 >> 0] |= 1 << (idx % 8)))
                            //[写入] >>> bool标志位
                            bFlag.forEach(bf => w.byte = bf)
                        })
                        break
                    case VT.byteArr:
                        fn2(val => w.byte = val)
                        break
                    case VT.uint8Arr:
                        fn2(val => w.uint8 = val)
                        break
                    case VT.uint16Arr:
                        fn2(val => w.uint16 = val)
                        break
                    case VT.uint32Arr:
                        fn2(val => w.uint32 = val)
                        break
                    case VT.uintArr:
                        fn2(val => w.uint = val)
                        break
                    case VT.int8Arr:
                        fn2(val => w.int8 = val)
                        break
                    case VT.int16Arr:
                        fn2(val => w.int16 = val)
                        break
                    case VT.int32Arr:
                        fn2(val => w.int32 = val)
                        break
                    case VT.intArr:
                        fn2(val => w.int = val)
                        break
                    case VT.floatArr:
                        fn2(val => w.float = val)
                        break
                    case VT.strArr:
                        fn2(val => w.str = val)
                        break
                    case VT.structArr:
                        let _struct = struct[idx][3]
                        fn2(val => enStruct(w, val, _struct))
                        break
                }
            }
        }
    }
    //[写入] >>> 标志位
    flag.forEach(f => writer.byte = f)
    //合并写入流
    writer.concat(w.recy())
}

//编码any类型对象值
function enVal(w: BinaryWriter, val: any) {
    //[写入] >>> 值类型 值(有的话)
    if (val instanceof Array) {
        if (val && val.length) {
            w.byte = AVT.arr
            enArr(w, val)
        } else {
            w.byte = AVT.e_arr
        }
    } else {
        switch (typeof val) {
            case "undefined":
                w.byte = AVT.undefined
                break
            case "boolean":
                w.byte = val ? AVT.true : AVT.false
                break;
            case "number":
                if (val) {
                    if (val % 1 === 0) {
                        w.byte = AVT.int
                        w.int = val
                    } else {
                        w.byte = AVT.float
                        w.float = val
                    }
                } else {
                    w.byte = AVT.e_int
                }
                break
            case "string":
                if (val) {
                    w.byte = AVT.str
                    w.str = val
                } else {
                    w.byte = AVT.e_str
                }
                break
            case "object":
                if (val) {
                    w.byte = AVT.obj
                    enObj(w, val)
                } else {
                    w.byte = AVT.e_obj
                }
                break;
        }
    }
}

//编码any类型对象
function enObj(writer: BinaryWriter, obj: any) {
    let w = BinaryWriter.alloc()
    //计算字段数量
    let len = 0
    for (let field in obj) {
        len++
        //[写入] >>> 字段名 值
        w.str = field
        enVal(w, obj[field])
    }
    //[写入] >>> 字段数量
    writer.uint = len
    //合并写入流
    writer.concat(w.recy())
}

//编码any类型数组
function enArr(w: BinaryWriter, arr: any[]) {
    //[写入] >>> 数组长度 值
    w.uint = arr.length
    arr.forEach(val => enVal(w, val))
}